#pragma once
#include <vector>
#include <d2d1.h>
#include "FindHelper.h"

static const int kMaxNumberOfCachedChunks = 64;
static const int kDefaultChunkSize = 1 << 14;
static const int kCacheLimitForFile = kDefaultChunkSize * kMaxNumberOfCachedChunks;

class FileHandler
{
	HANDLE mFile;
	std::vector<BYTE> mBuffer;
	std::vector<UINT> mChunkOffsets;

	// the pairs of indexes (prev, next)
	ByteVector mRecentList;
	int mNumberOfNodes;

	UINT mProtectedOffsets[2];

	LONGLONG mFileLength;
	UINT mChunkMax;

	int mChunkSize;
	int mAlignment;
	int mSectorSize;

	HANDLE mHeap;
	void* mAsyncData;
	BYTE* mAsyncBuffer;
	OVERLAPPED* mOverlappedPtr;
	int mAsyncDataSize;
	UINT mChunkBeingRead;
	bool mIsReadingAsync;

	bool mIsSearching;
	bool mWasSearchInterrupted;
	BOOL mIsDownwardSearch;
	UINT mChunkBeingSearched;
	int mSearchOffset;
	LONGLONG mSearchRange;
	int mSearchRangeLength;
	FindHelper mFindHelper;

public:
	FileHandler();
	~FileHandler();

	bool OpenFile(LPCTSTR path);
	LONGLONG GetFileLength() const;
	void MakeReadInAdvance(LONGLONG address, bool goingForward);
	bool AreTwoAddressesWithinOneCachedChunk(LONGLONG addr1, LONGLONG addr2) const;

	bool CopyBytes(LONGLONG address, BYTE* outBuffer, int& ioBuffLen);

	// used for preserving data that is shown on screen,
	// while the file is being read for other purposes (copying, finding)
	// it is assumed that n < mChunkSize
	// spacial case: if addr == -1 then the protection is removed
	void ProtectCachingForAddressRange(LONGLONG addr, int n);

	bool SetWhatVector(const WhatCharactersVector&);
	bool FindData(BOOL downwards, LONGLONG& ioAddr, int& outRangeLength);

	void StartFind(BOOL downwards, LONGLONG addressFrom);
	bool ContinueFind();
	bool LaunchAsyncReadingNextBlock();
	void AcceptCurrentAsyncBlock();
	HANDLE GetEventForAsyncReading() const;
	bool IsSearchFinished() const;
	bool WasSearchInterrupted() const;
	void SetSearchFinished();

	bool IsAtLastSearchBlock() const;
	void GotoNextSearchBlock();
	bool IsReadingAsync() const;
	bool IsDataCachedAtAddress(LONGLONG address) const;
	bool IsDataCachedAroundAddress(LONGLONG address, bool neighborChunkAbove) const;

	bool StopReading();
	bool GetRangeOfFoundBytes(LONGLONG& outRangeStart, int& outRangeLength);

private:
	void CloseFile();
	bool ResetAsyncParameters();

	// returns the minimal size of the data block
	int CalculatePointersInsideAsyncData();

	bool StartReadingChunk(UINT chunkNo);
	void AcceptAsyncData();
	UINT GetNextChunkIndex(UINT chunk, bool upward) const;

	// returns index in mChunkOffsets, or kInvalidIndex
	int ReadChunk(UINT chunk);
	const BYTE* GetChunkBufferForAddress(LONGLONG address);
};

